package com.liaoyifan.core.config;

import com.liaoyifan.core.constant.Constants;
import com.liaoyifan.core.constant.Properties;
import com.liaoyifan.core.opera.FeignTokenInterceptor;
import feign.Client;
import feign.Logger;
import feign.Request;
import feign.RequestInterceptor;
import feign.Retryer;
import feign.httpclient.ApacheHttpClient;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
import org.springframework.cloud.openfeign.loadbalancer.LoadBalancerFeignRequestTransformer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@SuppressWarnings({"SameReturnValue", "unused"})
@Configuration
@ConditionalOnProperty(value = Properties.FEIGN_ENABLE, havingValue = Constants.TRUE)
public class FeignConfiguration {

    @Value("${spring.application.name:-unknown}")
    private String applicationName;

    // 连接超时时间：60秒
    private static final int CONNECT_TIMEOUT = 60_000;
    // 响应超时时间：10分钟（根据业务需求调整，重要操作可适当延长）
    private static final int SOCKET_TIMEOUT = 600_000;

    @Bean
    @Primary
    public PoolingHttpClientConnectionManager connectionManager() {
        PoolingHttpClientConnectionManager connectionManager =
                new PoolingHttpClientConnectionManager();
        // 最大连接数（根据压测调整，通常为QPS*平均响应时间）
        connectionManager.setMaxTotal(500);
        // 单路由最大连接数（不超过MaxTotal的1/2）
        connectionManager.setDefaultMaxPerRoute(200);
        // 空闲连接验证周期（推荐5-10秒）
        connectionManager.setValidateAfterInactivity(5_000);
        return connectionManager;
    }

    @Bean
    @Primary
    public HttpClient httpClient() {
        RequestConfig requestConfig =
                RequestConfig.custom()
                        // 连接池获取连接超时（等待连接池分配连接的时间）
                        .setConnectionRequestTimeout(2_000)
                        // 连接超时 (被 Request.Options 覆盖)
                        .setConnectTimeout(CONNECT_TIMEOUT)
                        // 响应超时 (被 Request.Options 覆盖)
                        .setSocketTimeout(SOCKET_TIMEOUT)
                        .build();

        return HttpClientBuilder.create()
                .setConnectionManager(connectionManager())
                .setDefaultRequestConfig(requestConfig)
                // 连接存活时间（建议略大于LB刷新周期）
                .setConnectionTimeToLive(30, TimeUnit.SECONDS)
                // 定期清理空闲连接（每30秒清理一次）
                .evictIdleConnections(30, TimeUnit.SECONDS)
                // 清理过期连接
                .evictExpiredConnections()
                // 关闭自动重试（避免服务雪崩）
                .disableAutomaticRetries()
                // Keep-Alive策略（设置60秒连接复用）
                .setKeepAliveStrategy((response, context) -> 60_000)
                .build();
    }

    @Bean(name = "feignClient")
    @Primary
    public Client feignClient(
            LoadBalancerClient loadBalancerClient,
            LoadBalancerClientFactory loadBalancerClientFactory,
            List<LoadBalancerFeignRequestTransformer> transformers) {
        // 创建基础HTTP客户端
        Client delegate = new ApacheHttpClient(httpClient());
        // 包装成支持负载均衡的客户端
        return new FeignBlockingLoadBalancerClient(
                delegate, loadBalancerClient, loadBalancerClientFactory, transformers);
    }

    @Bean
    @Primary
    public Request.Options feignOptions() {
        return new Request.Options(
                CONNECT_TIMEOUT,
                TimeUnit.MILLISECONDS, // 连接超时(ms) - 建立 TCP 连接的最大时间
                SOCKET_TIMEOUT,
                TimeUnit.MILLISECONDS, // 响应超时(ms) - 从服务器获取响应的最大时间
                false); // 跟随重定向（生产环境建议关闭，由服务端控制跳转）
    }

    @Bean
    @Primary
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.BASIC;
    }

    @Bean
    @Primary
    public Retryer feignRetryer() {
        return Retryer.NEVER_RETRY;
    }

    @Bean
    public RequestInterceptor feignTokenInterceptor() {
        return new FeignTokenInterceptor(applicationName);
    }
}
